package com.service.kaptcha.rest;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.service.core.rest.BaseController;
import com.service.core.rest.response.BaseResponse;
import com.service.core.rest.response.OneResponse;
import com.service.kaptcha.interceptor.AuthenticationInterceptor;
import com.service.kaptcha.model.SysPicCode;
import com.service.kaptcha.request.PictureValidationCheckRequest;
import com.service.kaptcha.response.PictureValidationVO;
import com.service.kaptcha.service.ValidateCodeService;
import com.service.kaptcha.utils.Base64Utils;
import com.service.utility.common.NetworkUtil;
import com.service.utility.common.UUIDUtil;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Date;

@Controller("kaptchaController")
@RequestMapping(value = "/kaptcha")
@Api(tags = "验证码工具")
public class KaptchaController extends BaseController {

    @Autowired
    private DefaultKaptcha defaultKaptcha;

    @Autowired
    private ValidateCodeService validateCodeService;
    @Autowired
    private Environment evn;

    private static Integer MAX_DURATION;

    private Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 生成验证码
     */
    @ApiOperation(value = "生成验证码")
    @ApiResponse(code = 500, message = "服务器出错，生成验证码失败")
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public @ResponseBody
    OneResponse<PictureValidationVO> get(HttpServletRequest request, HttpServletResponse response) {
        String uuid = request.getAttribute(AuthenticationInterceptor.AUTHEN_KEN).toString();

        logger.info("生成验证码[uuid:{}]", uuid);

        //验证码
        String rightCode = defaultKaptcha.createText();

        //生成图片加密后Base64字符串
        String imgBase64String;
        try {
            imgBase64String = getImgBase64String(rightCode);
        } catch (IOException e) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            logger.info("生成验证码失败[uuid:{}]", uuid);
            return new OneResponse<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部出错", null);
        }

        //保存记录
        savePictureValidationCode(NetworkUtil.getIpAddress(request), uuid, rightCode);

        //返回响应内容
        PictureValidationVO pictureValidationVO = new PictureValidationVO();
        pictureValidationVO.setUuid(uuid);
        pictureValidationVO.setImgBase64(imgBase64String);

        logger.info("生成的验证码[uuid:{}]", uuid);
        return successQueryOneResponse(pictureValidationVO);
    }

    /**
     * 校验验证码
     */
    @ApiOperation(value = "校验验证码")
    @ApiResponses({@ApiResponse(code = 404, message = "验证码不存在"),
            @ApiResponse(code = 400, message = "验证码不正确")})
    @RequestMapping(value = "/validate", method = RequestMethod.POST)
    public @ResponseBody
    BaseResponse validate(@ApiParam @RequestBody @Validated PictureValidationCheckRequest checkRequest,
                          HttpServletRequest request,
                          HttpServletResponse response) {
        String uuid = request.getAttribute(AuthenticationInterceptor.AUTHEN_KEN).toString();
        logger.info("校验验证码[uuid:{}, checkRequest:{}]", uuid, checkRequest);

        //初始化有效期
        initMaxDuration();

        //根据uuid获取验证码
        SysPicCode pictureValidationCodeEO = validateCodeService.getByUUID(checkRequest.getUuid());

        if (pictureValidationCodeEO == null) {
            logger.info("校验验证码失败，验证码不存在[uuid:{}, checkRequest:{}]", uuid, checkRequest);
            return notFoundOneResponse("验证码不存在", response);
        }

        //取出验证码
        String  input = checkRequest.getCode();
        String right = pictureValidationCodeEO.getRightCode();

        //验证码错误
        if (!isRight(input, right, uuid)) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());

            logger.info("校验验证码失败，验证码错误[uuid:{}, inputCode:{}, rightCode:{}]", uuid, input, right);
            return new BaseResponse(HttpStatus.BAD_REQUEST.value(), "验证码不正确");
        }

        //验证码已过期
        if (isExpire(pictureValidationCodeEO.getCreateDate())) {
            response.setStatus(HttpStatus.BAD_REQUEST.value());

            logger.info("校验验证码失败，验证码已过期[uuid:{}, inputCode:{}, rightCode:{}]", uuid, input, right);
            return new BaseResponse(HttpStatus.BAD_REQUEST.value(), "验证码已过期");
        }

        logger.info("校验验证码成功[uuid:{}, inputCode:{}, rightCode:{}]", uuid, input, right);
        return new BaseResponse(HttpStatus.OK.value(), "验证成功");
    }


    /**
     * 校验图片验证码
     *
     * @param input 输入的验证码
     * @param right 正确的验证码
     * @param uuid  uuid
     * @return
     */
    private boolean isRight(String input, String right, String uuid) {
        input = input.toLowerCase();
        right = right.toLowerCase();

        logger.info("校验验证码，转小写[uuid:{}, inputCode:{}, rightCode:{}]", uuid, input, right);
        return input.equals(right);
    }

    /**
     * 初始化配置变量
     */
    private void initMaxDuration() {
        if (MAX_DURATION == null) {
            MAX_DURATION = Integer.valueOf(evn.getProperty("project.valid-duration"));
        }
    }

    /**
     * 校验验证码是否过期
     *
     * @param createDate
     * @return
     */
    private boolean isExpire(Date createDate) {
        return (System.currentTimeMillis() - createDate.getTime()) > MAX_DURATION * 1000;
    }


    /**
     * 通过生成的验证码字符串生成图片Base64加密后字符串
     *
     * @param rightCode 验证码内容
     * @return
     * @throws IOException
     */
    private String getImgBase64String(String rightCode) throws IOException {
        //将验证码图片转换成字节流
        BufferedImage challenge = defaultKaptcha.createImage(rightCode);
        ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
        ImageIO.write(challenge, "jpg", jpegOutputStream);
        return Base64Utils.getImageBase64String(jpegOutputStream.toByteArray());
    }

    /**
     * 保存数据
     *
     * @param addressIp 请求的ip
     * @param uuid      uuid
     * @param rightCode 验证码正确的内容
     */
    private void savePictureValidationCode(String addressIp, String uuid, String rightCode) {
        //将记录保存到数据库
        Date current = new Date();
        SysPicCode pictureValidationCodeEO = new SysPicCode();
        pictureValidationCodeEO.setRightCode(rightCode);
        pictureValidationCodeEO.setAddressIp(addressIp);
        pictureValidationCodeEO.setUuid(uuid);
        pictureValidationCodeEO.setId(UUIDUtil.randomUUID());
        pictureValidationCodeEO.setCreateDate(current);
        pictureValidationCodeEO.setModifyDate(current);
        validateCodeService.save(pictureValidationCodeEO);
    }
}